//
//  LCCardCameraController.swift
//  dj-learn-Swift
//
//  Created by jie sun on 2022/5/21.
//  Copyright © 2022 S.J. All rights reserved.
//
import UIKit
import AVKit
import RxSwift

class LCCardCameraController: UIViewController {
    var disposeBag = DisposeBag()
  
    var captureDevice: AVCaptureDevice!
    var captureSession = AVCaptureSession()
    var previewLayer: AVCaptureVideoPreviewLayer!

    // 相机操作视图
    var cameraView = LCCardCameraView()
    // 是否拍照
    var isTakePhoto = false
    
    var successTake: ((_ image: UIImage) -> Void)?

    // MARK: - Override
    // 隐藏状态栏
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    // 强制竖屏
    override var shouldAutorotate: Bool {
        get {
            return false
        }
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        get {
            return .portrait
        }
    }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        get {
            return .portrait
        }
    }
    
    // View Cycle
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.capturePhoto()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.configSubiews()
        self.configConstraints()
        self.configActions()
    }
    
    // MARK: - Config View
    fileprivate func configActions() {
        // 拍照按钮点击
        self.cameraView.takePhotoButton.rx.tap.subscribe(onNext: {
            // 防止连点
            self.cameraView.takePhotoButton.isEnabled = false
            self.isTakePhoto = true
        }).disposed(by: self.disposeBag)
        
        // 关闭按钮点击
        self.cameraView.closeButton.rx.tap.subscribe(onNext: {
            // 结束捕获图片，并退出
            self.stopCaptureSession()
            self.dismiss(animated: true)
        }).disposed(by: self.disposeBag)
        
        // 手电筒按钮点击
        self.cameraView.torchButton.rx.tap.subscribe(onNext: {
            self.changeTorchMode()
        }).disposed(by: self.disposeBag)
    }
    
    fileprivate func configSubiews() {}
    
    fileprivate func configConstraints() {
        self.view.addSubview(self.cameraView)
        self.cameraView.snp.makeConstraints { make in
            make.edges.equalTo(self.view)
        }
    }
    
    // MARK: - Private method
    // MARK: 捕获图片
    fileprivate func capturePhoto() {
        self.isTakePhoto = false
        // 1、获取拍照相机设备
        guard let device = self.requestCaptureDevice() else {
            // 无设备提示
            self.alertNoDevice()
            return
        }
        self.captureDevice = device
        
        // 2、检查是否有拍照权限
        self.requestAuth {[weak self] access in
            guard let self = self else { return }
            if !access { // 无权限提示
                self.alertNoCameraPermission()
                return
            }
        }
        
        // 3、创建 Capture Session
        self.captureSession = AVCaptureSession()
        
        // 4、设置 Capture Session
        self.configCaptureSession()
        
        // 5、开始捕获图片
        self.captureSession.startRunning()
        self.cameraView.takePhotoButton.isEnabled = true
    }
    
    
    // MARK: 查询相机权限
    fileprivate func requestAuth(complete: @escaping (_ access: Bool) -> Void) {
        switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video) {
        case .authorized:
            complete(true)
            break
        case .denied, .restricted, .notDetermined:
            AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted) in
                complete(granted)
            })
            break
        @unknown default:
            complete(false)
            break
        }
    }
    
    // MARK: 获取相机设备
    fileprivate func requestCaptureDevice() -> AVCaptureDevice? {
        if #available(iOS 10.0, *) {
            return AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices.first
        } else {
            // development, need to test this on iOS 8
            return AVCaptureDevice.default(for: AVMediaType.video)
        }
    }
    
    // MARK: 打开/关闭手电筒
    fileprivate func changeTorchMode() {
        if self.captureDevice.hasTorch, self.captureDevice.isTorchAvailable {
            do {
                try self.captureDevice.lockForConfiguration()
                switch self.captureDevice.torchMode {
                case .off:
                    self.captureDevice.torchMode = .on
                case .on:
                    self.captureDevice.torchMode = .off
                default:
                    self.captureDevice.torchMode = .auto
                }
                self.captureDevice.unlockForConfiguration()
            } catch {
                print(error.localizedDescription)
            }
        }
    }
    
    // MARK: 配置 Capture Session
    private func configCaptureSession() {
        // 决定获取图片的像素
        self.captureSession.sessionPreset = AVCaptureSession.Preset.vga640x480
        // 设备输入
        do {
            let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
            self.captureSession.addInput(captureDeviceInput)
        } catch {
            print(error.localizedDescription)
        }
        
        // 图片显示图层
        self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        self.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        self.view.layer.addSublayer(self.previewLayer)
        self.previewLayer.frame = self.view.bounds
        self.view.bringSubviewToFront(self.cameraView)
        
        // 输出数据
        let dataOutput = AVCaptureVideoDataOutput()
        dataOutput.videoSettings = [((kCVPixelBufferPixelFormatTypeKey as NSString) as String):NSNumber(value:kCVPixelFormatType_32BGRA)]
        dataOutput.alwaysDiscardsLateVideoFrames = true
        if self.captureSession.canAddOutput(dataOutput) {
            self.captureSession.addOutput(dataOutput)
        }
        let queue = DispatchQueue(label: "com.jea.cumstom.camera")
        dataOutput.setSampleBufferDelegate(self, queue: queue)
        
        self.captureSession.commitConfiguration()
    }
    
    // MARK: 从帧数据中获取图片
    func getImageFromSampleBuffer(buffer:CMSampleBuffer) -> UIImage? {
        if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) {
            let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
            let context = CIContext()
            let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))
            
            print("imageRect: \(imageRect)")
            
            var orientation = UIImage.Orientation.up
            switch UIDevice.current.orientation {
            case .landscapeLeft:
                orientation = .left
            case .landscapeRight:
                orientation = .right
            case .portraitUpsideDown:
                orientation = .down
            default:
                orientation = .up
            }
            if let image = context.createCGImage(ciImage, from: imageRect) {
                return UIImage(cgImage: image, scale: UIScreen.main.scale, orientation: orientation)
            }
        }
        return nil
    }
    
    // MARK:  结束捕获图片会话
    func stopCaptureSession() {
        self.captureSession.stopRunning()
        
        if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
            for input in inputs {
                self.captureSession.removeInput(input)
            }
        }
    }
    
    // MARK: - Alert
    // 提示没有相机
    fileprivate func alertNoDevice() {
        let alert = UIAlertController(title: "温馨提示", message: "无法获取到设备相机，请确认您的设备是否有相机或相机是否已损坏", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确定", style: .cancel, handler: {_ in
            self.navigationController?.dismiss(animated: true, completion: nil)
        }))
        self.present(alert, animated: true, completion: nil)
    }
    
    // 提示没有相机权限
    fileprivate func alertNoCameraPermission() {
        let alert = UIAlertController(title: "温馨提示", message: "无法打开相机，请确认您已经打开相机权限", preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction(title: "ok", style: .default, handler: {_ in
            self.navigationController?.popToRootViewController(animated: true)
        }))
        self.present(alert, animated: true, completion: nil)
    }
    
}

extension LCCardCameraController: AVCaptureVideoDataOutputSampleBufferDelegate {
    // 每一帧图片数据的输出代理方法
    // 6、拍照按钮点击后(isTakePhoto == true), 获取当前这一帧作为拍照图片
    func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // 强制竖屏读帧
        if connection.isVideoOrientationSupported {
            connection.videoOrientation = .portrait
        }
        if self.isTakePhoto {
            self.isTakePhoto = false
            // 7、获取帧图片
            if let image = self.getImageFromSampleBuffer(buffer: sampleBuffer) {
                DispatchQueue.main.async {
                    // 8、获取证件显示框在帧图片中的 frame
                    let scale = image.size.height / self.previewLayer.frame.height
                    let rect = self.cameraView.convert(self.cameraView.boxImgView.frame, to: self.view)
                    let y = rect.origin.y * scale
                    let height = rect.height * scale
                    let width = rect.width * scale
                    let x = 0.5 * (image.size.width - width)
                    let bounds = CGRect(x: x, y: y, width: width, height: height)
                    // 9、将证件框范围内的图片截取出来
                    let resultImage = image.imageByCropToRect(rect: bounds, scale: true)
                    // 10、图片旋转
                    let resultImg = resultImage.sj_rotate(with: -90.0)
                    
                    guard let successTake = self.successTake, let resultImg = resultImg else { return }
                    successTake(resultImg)
                    // 11、关闭图像捕获会话
                    self.stopCaptureSession()
                    self.dismiss(animated: true)
                }
            }
        }
    }
}
